﻿using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;

/* 支持Unity自带协程也支持扩展协程
 * 使用方法：
 * 1. 如何使用
 * - 使用StartCoroutine来开启一个协程并返回WaitForCoroutine，用WaitForCoroutine的IsReady()可以判断协程是否执行完毕
 * - 使用StopCoroutine来关闭协程
 * 2. 如何扩展WaitForXXX
 * - 写一个类实现IWaitable接口，或者继承自CWaitable
 */

namespace Memo.Electricity.Engine
{
	public interface IWaitable
	{
		bool IsReady();
	}

	public class CWaitable : IWaitable
	{
		protected Func<bool> ready;
		public bool IsReady() => ready();

		public static CWaitable operator +(CWaitable a, CWaitable b)
			=> new CWaitable { ready = () => a.IsReady() || b.IsReady() };

		public static CWaitable operator *(CWaitable a, CWaitable b)
			=> new CWaitable { ready = () => a.IsReady() && b.IsReady() };
	}

	public sealed class WaitForCoroutine : CWaitable
	{
		public WaitForCoroutine()
			=> ready = () => CCoroutineMgr.Inst?.GetCoroutine(this) == null;
	}

	public class CCoroutineMgr : CSingletonBehaviour<CCoroutineMgr>
	{
		#region 对外接口

		/// <summary>
		/// 开启一个协程
		/// </summary>
		/// <param name="iter">协程函数</param>
		/// <returns>一个Waitable标记协程是否执行完毕</returns>
		public new WaitForCoroutine StartCoroutine(IEnumerator iter)
		{
			WaitForCoroutine wait = new WaitForCoroutine();
			Coroutine co = base.StartCoroutine(CoScheduler(iter, wait));
			mCoroutines.Add(wait, co);
			return wait;
		}

		/// <summary>
		/// 停止一个协程
		/// </summary>
		/// <param name="wait"></param>
		public void StopCoroutine(WaitForCoroutine wait)
		{
			if (!wait.IsReady())
			{
				base.StopCoroutine(mCoroutines[wait]);
				mCoroutines.Remove(wait);
			}
		}

		/// <summary>
		/// 停止所有协程
		/// </summary>
		public new void StopAllCoroutines()
		{
			base.StopAllCoroutines();
			mCoroutines.Clear();
		}

		/// <summary>
		/// 获取Wait对应的Coroutine实例
		/// </summary>
		public Coroutine GetCoroutine(WaitForCoroutine wait)
		{
			if (mCoroutines.ContainsKey(wait))
			{
				return mCoroutines[wait];
			}
			else
			{
				return null;
			}
		}

		#endregion

		private Dictionary<WaitForCoroutine, Coroutine> mCoroutines = new Dictionary<WaitForCoroutine, Coroutine>();

		//协程调度
		private IEnumerator CoScheduler(IEnumerator iter, WaitForCoroutine wait)
		{
			while (iter.MoveNext())
			{
				var res = iter.Current;
				if (res is IWaitable) //iter中返回了一个IWaitable
				{
					IWaitable waitable = res as IWaitable;
					while (!waitable.IsReady())
						yield return null;
				}
				else //iter中返回了一个Unity自带的协程条件
				{
					yield return res;
				}
			}
			mCoroutines.Remove(wait);
		}

		//释放资源
		private void OnDestroy()
		{
			base.StopAllCoroutines();
			mCoroutines.Clear();
		}

	}
}